﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MongoDB;
using MongoDB.Driver;
using MongoDB.Bson;
using System.Configuration;
using System.IO;
using Base.Common;
using Norm;
using Norm.Linq;
using Norm.Responses;
using Norm.GridFS;


namespace Base.Document
{
    /// <summary>
    /// In this class we have used two different MongoDB driver
    /// 1) Norm driver (at this time the Gridfs is not fully supported)
    /// 2) Driver from 10gen the creators of MongoDB. This is the official driver. At this time no support for LINQ
    /// <remarks>
    /// http://www.codevoyeur.com/Articles/20/A-NoRM-MongoDB-Repository-Base-Class.aspx
    /// </remarks>
    /// </summary>
    public class ConcreteMongoDao : IDmsDao
    {

        private MongoServer _provider10Gen;
        private IMongo _providerNorm;
        public MongoDB.Driver.MongoDatabase database10Gen { get { return this._provider10Gen.GetDatabase(ConfigurationSettings.AppSettings.Get("mongo_db_name")); } }
        //public Norm.MongoDatabase databaseNorm { get { return this._providerNorm.DatabaseConfigurationSettings.AppSettings.Get("mongo_db_name")); } }

        public ConcreteMongoDao()
        {
            
            //this looks for a connection string in your Web.config
            _provider10Gen = MongoServer.Create(ConfigurationSettings.AppSettings.Get("mongo_con_string"));
            _providerNorm = Mongo.Create(ConfigurationSettings.AppSettings.Get("mongo_con_string"));
        }

        public void CommitChanges()
        {
            //mongo isn't transactional in this way
        }

        public void Delete<T>(System.Linq.Expressions.Expression<Func<T, bool>> expression) where T : class, new()
        {
            var items = All<T>().Where(expression);
            foreach (T item in items)
            {
                Delete(item);
            }
        }

        public void Delete<T>(T item) where T : class, new()
        {
            _providerNorm.GetCollection<T>().Delete(item);
         
        }

        public void DeleteAll<T>() where T : class, new()
        {
           throw new NotImplementedException();
        }

        public T Single<T>(System.Linq.Expressions.Expression<Func<T, bool>> expression) where T : class, new()
        {
            return All<T>().Where(expression).SingleOrDefault();
        }

        public IQueryable<T> All<T>() where T : class, new()
        {
            return _providerNorm.GetCollection<T>().AsQueryable();
        }

        public void Add<T>(T item) where T : class, new()
        {
            _providerNorm.GetCollection<T>().Insert(item);           
        }

     

        public void Add<T>(IEnumerable<T> items) where T : class, new()
        {
            foreach (T item in items)
            {
                Add(item);
            }
        }

        public void Update<T>(T item) where T : class, new()
        {
            _providerNorm.GetCollection<T>().UpdateOne(item, item);           
        }

        //Helper for using map reduce in mongo
        public T MapReduce<T>(string map, string reduce)
        {
            throw new NotImplementedException();
            //T result = default(T);
            //MapReduce mr = database.CreateMapReduce();

            //MapReduceResponse response =
            //    mr.Execute(new MapReduceOptions(typeof(T).Name)
            //    {
            //        Map = map,
            //        Reduce = reduce
            //    });
            //IMongoCollection<MapReduceResult<T>> coll = response.GetCollection<MapReduceResult<T>>();
            //MapReduceResult<T> r = coll.Find().FirstOrDefault();
            //result = r.Value;

            //return result;
        }

        #region GridFS
        public BsonValue AddFile(Stream fs, string fileName)
        {                     
            var gridFsInfo = database10Gen.GridFS.Upload(fs, fileName.ToLower());
           
            return gridFsInfo.Id;
        }

        public BsonValue AddFile(Stream fs, string fileName, BsonDocument metaData)
        {
            var gridFsInfo = database10Gen.GridFS.Upload(fs, fileName.ToLower());
            gridFsInfo.Metadata.Add(metaData);
            return gridFsInfo.Id;            
        }

        public byte[] FindByQuery(MongoDB.Driver.IMongoQuery query, Int32 version)
        {
            var file =  (version==0) ? database10Gen.GridFS.FindOne(query) : database10Gen.GridFS.FindOne(query,version);
            using (var stream = file.OpenRead())
            {
                return Base.Utils.CommonUtils.GetAsByteArray(stream);
            }            
           
        }


        public bool FileExists(MongoDB.Driver.IMongoQuery query)
        {
          return database10Gen.GridFS.Exists(query);         
        }

        public bool FileExists(string fileName)
        {
            return database10Gen.GridFS.Exists(fileName.ToLower());
        }

        public void DeleteFile(string fileName)
        {
            database10Gen.GridFS.Delete(fileName.ToLower());
        }

        public void DeleteFile(MongoDB.Driver.IMongoQuery query)
        {
            database10Gen.GridFS.Delete(query);
        }

      

        public byte[] FindByName(string fileName, Int32 version)
        {
           
            var file = (version == 0) ? database10Gen.GridFS.FindOne(fileName.ToLower()) : database10Gen.GridFS.FindOne(fileName.ToLower(), version);
            using (var stream = file.OpenRead())
            {
                return Base.Utils.CommonUtils.GetAsByteArray(stream);
            }        
        }

#endregion


        public void Dispose()
        {
            _providerNorm.Dispose();
        }   
    }

}
